Factory Pattern

在策略模式曾提到过,我们需要面向接口编程,而不是具体类。但是无法避免的是,我们需要使用new方法来创建对象,这时我们不得不使用具体类。例如:

1
2
3
4
5
6
7
if(picnic) {
duck = new MalldarDuck();
} else if(hunting) {
duck = new DecoyDuck();
} else {
duck = new RubberDuck();
}

这样一旦有变化或扩展,我们需要修改所有这些引用了具体类new对象的代码,造成系统难以维护和修改。代码无法对修改关闭。

同样的,我们应该分离变化的部分。在这里new对象部分是可能发生变化的部分,例如新增具体累,删除具体类,或者构造函数变化等。

Design_pattern_factory_1

简单工厂模式

最直接的方法就是将创建对象移到一个专职创建披萨的对象中,称这个对象为工厂。工厂(factory)处理创建对象的细节,这个如果客户需要使用产品,只需要通过工厂来获得产品直接使用,不需要自己使用具体类来进行创建。创建代码集中到一个工厂类中,也方便集中同一管理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SimplePizzaFactory {

public Pizza createPizza(String type) {
Pizza pizza = null;

if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("pepperoni")) {
pizza = new PepperoniPizza();
} else if (type.equals("clam")) {
pizza = new ClamPizza();
} else if (type.equals("veggie")) {
pizza = new VeggiePizza();
}
return pizza;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class PizzaStore {
SimplePizzaFactory factory;

public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}

public Pizza orderPizza(String type) {
Pizza pizza;

pizza = factory.createPizza(type);

pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();

return pizza;
}

}

定义简单工厂

上面用到的是简单工厂,其实并不是一个设计模式。因为其只是将问题搬到了另一个对象,当需要新增实现类时,仍然需要修改这个工厂类。但是由于其简单性,在变化有限的情况下仍是很好的选择。

Design_pattern_factory_2

工厂方法模式

在上面的简单工厂模式中,但需要新增产品类时,我们任然需要修改简单工厂类,对修改没有关闭。这里介绍的工厂方法模式可以帮助我们满足开闭原则。

对于工厂方法模式,所有工厂模式都用来封装对象的创建。工厂方法模式(Factory Method Pattern)让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。

Design_pattern_factory_3

另一种理解方式是,平行的类层级。

Design_pattern_factory_5

定义工厂方法模式

工厂方法模式定义了一个创建对象的接口,但由其子类决定具体实例化的是哪一个产品。工厂方法让类把实例化推迟到子类。我们选择那个子类工厂,也就是选择了需要获得产品类。

这样当我们需要新增产品时,只需要实现这个产品类(也可是多个产品类,但是同属于一个基类,比如都是pizza),然后实现生产这个产品的工厂类,继承自工厂方法模式。就可以通过这个工厂获得这个新产品,无需修改抽象工厂类和任何其他类的代码。

Design_pattern_factory_6

定义工厂方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class PizzaStore {

abstract Pizza createPizza(String item);

public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
System.out.println("--- Making a " + pizza.getName() + " ---");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();

void prepare() {
System.out.println("Preparing " + name);
System.out.println("Tossing dough...");
System.out.println("Adding sauce...");
System.out.println("Adding toppings: ");
for (int i = 0; i < toppings.size(); i++) {
System.out.println(" " + toppings.get(i));
}
}

void bake() {
System.out.println("Bake for 25 minutes at 350");
}

void cut() {
System.out.println("Cutting the pizza into diagonal slices");
}

void box() {
System.out.println("Place pizza in official PizzaStore box");
}

public String getName() {
return name;
}

public String toString() {
StringBuffer display = new StringBuffer();
display.append("---- " + name + " ----\n");
display.append(dough + "\n");
display.append(sauce + "\n");
for (int i = 0; i < toppings.size(); i++) {
display.append((String )toppings.get(i) + "\n");
}
return display.toString();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class NYPizzaStore extends PizzaStore {

Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new NYStyleCheesePizza();
} else if (item.equals("veggie")) {
return new NYStyleVeggiePizza();
} else if (item.equals("clam")) {
return new NYStyleClamPizza();
} else if (item.equals("pepperoni")) {
return new NYStylePepperoniPizza();
} else return null;
}
}

public class ChicagoPizzaStore extends PizzaStore {

Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new ChicagoStyleCheesePizza();
} else if (item.equals("veggie")) {
return new ChicagoStyleVeggiePizza();
} else if (item.equals("clam")) {
return new ChicagoStyleClamPizza();
} else if (item.equals("pepperoni")) {
return new ChicagoStylePepperoniPizza();
} else return null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class ChicagoStylePepperoniPizza extends Pizza {
public ChicagoStylePepperoniPizza() {
name = "Chicago Style Pepperoni Pizza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";

toppings.add("Shredded Mozzarella Cheese");
toppings.add("Black Olives");
toppings.add("Spinach");
toppings.add("Eggplant");
toppings.add("Sliced Pepperoni");
}

void cut() {
System.out.println("Cutting the pizza into square slices");
}
}

public class ChicagoStyleCheesePizza extends Pizza {

public ChicagoStyleCheesePizza() {
name = "Chicago Style Deep Dish Cheese Pizza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";

toppings.add("Shredded Mozzarella Cheese");
}

void cut() {
System.out.println("Cutting the pizza into square slices");
}
}

public class ChicagoStyleClamPizza extends Pizza {
public ChicagoStyleClamPizza() {
name = "Chicago Style Clam Pizza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";

toppings.add("Shredded Mozzarella Cheese");
toppings.add("Frozen Clams from Chesapeake Bay");
}

void cut() {
System.out.println("Cutting the pizza into square slices");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class NYStylePepperoniPizza extends Pizza {

public NYStylePepperoniPizza() {
name = "NY Style Pepperoni Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";

toppings.add("Grated Reggiano Cheese");
toppings.add("Sliced Pepperoni");
toppings.add("Garlic");
toppings.add("Onion");
toppings.add("Mushrooms");
toppings.add("Red Pepper");
}
}
public class NYStyleCheesePizza extends Pizza {

public NYStyleCheesePizza() {
name = "NY Style Sauce and Cheese Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";

toppings.add("Grated Reggiano Cheese");
}
}
public class NYStyleClamPizza extends Pizza {

public NYStyleClamPizza() {
name = "NY Style Clam Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";

toppings.add("Grated Reggiano Cheese");
toppings.add("Fresh Clams from Long Island Sound");
}
}

另外可以对比一下,不使用工厂方法的实现,尽管代码上可能没有上面的抽象复杂,但是其在系统变化是非常难以维护。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class DependentPizzaStore {

public Pizza createPizza(String style, String type) {
Pizza pizza = null;
if (style.equals("NY")) {
if (type.equals("cheese")) {
pizza = new NYStyleCheesePizza();
} else if (type.equals("veggie")) {
pizza = new NYStyleVeggiePizza();
} else if (type.equals("clam")) {
pizza = new NYStyleClamPizza();
} else if (type.equals("pepperoni")) {
pizza = new NYStylePepperoniPizza();
}
} else if (style.equals("Chicago")) {
if (type.equals("cheese")) {
pizza = new ChicagoStyleCheesePizza();
} else if (type.equals("veggie")) {
pizza = new ChicagoStyleVeggiePizza();
} else if (type.equals("clam")) {
pizza = new ChicagoStyleClamPizza();
} else if (type.equals("pepperoni")) {
pizza = new ChicagoStylePepperoniPizza();
}
} else {
System.out.println("Error: invalid type of pizza");
return null;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}

审视对象依赖

当我们直接实例化一个类是,就是在依赖它的具体类。如果把上面的代码依赖画成一张图:

Design_pattern_factory_7

设计原则:
要依赖抽象,不要依赖具体类。

在应用工厂方法后,类图会变成如下。

Design_pattern_factory_8

抽象工厂模式

抽象工厂本质上就是一中工厂方法模式的加强版。在工厂方法中,一个工厂方法抽象了一种基类型产品的建立,并交给具体子类负责创建产品。也就是一个工厂方法负责一类基类型产品创建。在有的时候,会有一系列基类型产品同属于一个产品家族,并且最好由一个具体工厂来创建。抽象工厂模式就提供了这种解决方案。

抽象工厂模式¥提供了一个接口(设计模式中接口即可以指实际的接口,也可指抽象类),用于创建相关或依赖对象的家族,而不需要明确指定具体类。

也就是抽象工厂申明了几个工厂方法,各个工厂方法创建的类型属于同一个产品族。

Design_pattern_factory_9

Design_pattern_factory_10

定义抽象工厂

1
2
3
4
5
6
7
8
9
10
public interface PizzaIngredientFactory {

public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClam();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {

public Dough createDough() {
return new ThinCrustDough();
}

public Sauce createSauce() {
return new MarinaraSauce();
}

public Cheese createCheese() {
return new ReggianoCheese();
}

public Veggies[] createVeggies() {
Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
return veggies;
}

public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}

public Clams createClam() {
return new FreshClams();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public abstract class Pizza {
String name;

Dough dough;
Sauce sauce;
Veggies veggies[];
Cheese cheese;
Pepperoni pepperoni;
Clams clam;

abstract void prepare();

void bake() {
System.out.println("Bake for 25 minutes at 350");
}

void cut() {
System.out.println("Cutting the pizza into diagonal slices");
}

void box() {
System.out.println("Place pizza in official PizzaStore box");
}

void setName(String name) {
this.name = name;
}

String getName() {
return name;
}

public String toString() {
StringBuffer result = new StringBuffer();
result.append("---- " + name + " ----\n");
if (dough != null) {
result.append(dough);
result.append("\n");
}
if (sauce != null) {
result.append(sauce);
result.append("\n");
}
if (cheese != null) {
result.append(cheese);
result.append("\n");
}
if (veggies != null) {
for (int i = 0; i < veggies.length; i++) {
result.append(veggies[i]);
if (i < veggies.length-1) {
result.append(", ");
}
}
result.append("\n");
}
if (clam != null) {
result.append(clam);
result.append("\n");
}
if (pepperoni != null) {
result.append(pepperoni);
result.append("\n");
}
return result.toString();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;

public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}

void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}

public class ClamPizza extends Pizza {
PizzaIngredientFactory ingredientFactory;

public ClamPizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}

void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
clam = ingredientFactory.createClam();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class NYPizzaStore extends PizzaStore {

protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory =
new NYPizzaIngredientFactory();

if (item.equals("cheese")) {

pizza = new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese Pizza");

} else if (item.equals("veggie")) {

pizza = new VeggiePizza(ingredientFactory);
pizza.setName("New York Style Veggie Pizza");

} else if (item.equals("clam")) {

pizza = new ClamPizza(ingredientFactory);
pizza.setName("New York Style Clam Pizza");

} else if (item.equals("pepperoni")) {

pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("New York Style Pepperoni Pizza");

}
return pizza;
}
}

比较工厂方法和抽象工厂

Design_pattern_factory_11

Design_pattern_factory_12